package com.time.timespace.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageInfo;
import com.time.timespace.commons.constant.BaseConstants;
import com.time.timespace.commons.dto.PageRequest;
import com.time.timespace.commons.utils.ExcelExportUtil;
import com.time.timespace.commons.utils.PageUtil;
import com.time.timespace.domain.dto.CategoryCountDTO;
import com.time.timespace.domain.dto.ChargeDTO;
import com.time.timespace.domain.entity.Category;
import com.time.timespace.domain.entity.Charge;
import com.time.timespace.mapper.CategoryMapper;
import com.time.timespace.mapper.ChargeMapper;
import com.time.timespace.service.CategoryService;
import com.time.timespace.service.ChargeService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ChargeServiceImpl extends ServiceImpl<ChargeMapper, Charge> implements ChargeService {

    private final ChargeMapper chargeMapper;
    private final CategoryMapper categoryMapper;
    private final CategoryService categoryService;

    @Autowired
    public ChargeServiceImpl(ChargeMapper chargeMapper, CategoryMapper categoryMapper, CategoryService categoryService) {
        this.chargeMapper = chargeMapper;
        this.categoryMapper = categoryMapper;
        this.categoryService = categoryService;
    }

    @Override
    public PageInfo<Charge> selectChargeById(PageRequest pageRequest, Long createdBy) {
        PageUtil.doPage(pageRequest);
        List<Charge> chargeList = chargeMapper.selectChargeById(createdBy);
        return new PageInfo<>(chargeList);
    }

    @Override
    public PageInfo<Charge> selectCharge(PageRequest pageRequest, Charge charge) {
        PageUtil.doPage(pageRequest);
//        LambdaQueryWrapper<Charge> wrapper = new LambdaQueryWrapper<>();
//        if (Objects.nonNull(charge.getChargeId())) {
//            wrapper.eq(Charge::getChargeId, charge.getChargeId());
//        }
//        if (StringUtils.isNotBlank(charge.getCategory())) {
//            wrapper.eq(Charge::getCategory, charge.getCategory());
//        }
//        if (StringUtils.isNotBlank(charge.getRemarks())) {
//            wrapper.eq(Charge::getRemarks, charge.getRemarks());
//        }
//        if (Objects.nonNull(charge.getDateStr())) {
//            // 追加 and 条件，正常写sql即可
//            wrapper.apply("date_format(date, '%Y-%m-%d') = '" + charge.getDateStr() + "'");
//        }
//        // 查询月度支出信息
//        if (Objects.nonNull(charge.getMonthStr())) {
//            wrapper.apply("date_format(date, '%Y-%m') = '" + charge.getMonthStr() + "'");
//        }
//        // 查询年度支出信息
//        if (Objects.nonNull(charge.getYearStr())) {
//            wrapper.apply("date_format(date, '%Y') = '" + charge.getYearStr() + "'");
//        }
//        wrapper.eq(Charge::getDeleteFlag, BaseConstants.Flag.N);
//        wrapper.orderBy(true, false, Charge::getChargeId);
//        List<Charge> chargeList = chargeMapper.selectList(wrapper);
        List<Charge> chargeList = chargeMapper.selectChargeByCondition(charge);
        if (CollectionUtils.isNotEmpty(chargeList)) {
            Map<String, String> categoryMap = categoryMapper.selectList(new QueryWrapper<>())
                    .stream().collect(HashMap::new, (m, v) -> m.put(v.getValue(), v.getLabel()), HashMap::putAll);
            chargeList.forEach(c -> {
                c.setType(BaseConstants.TYPE_MAP.get(c.getType()));
                c.setCategory(categoryMap.get(c.getCategory()));
            });
        }
        System.out.println(new PageInfo<>(chargeList));
        return new PageInfo<>(chargeList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Charge saveCharge(Charge charge) {
        if (Objects.isNull(charge.getChargeId())) {
            // 新增流水号
            String dateStr = DateFormatUtils.format(charge.getDate(), BaseConstants.Pattern.DATE_STRING);
            LambdaQueryWrapper<Charge> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(Charge::getDate, charge.getDate());
            wrapper.eq(Charge::getCreatedBy, charge.getCreatedBy());
            // 这里无需限制是否删除，流水号直接后延
            Long count = chargeMapper.selectCount(wrapper);
            String num = String.format("%04d", count + 1);
            String chargeNum = "T-" + dateStr + num;
            charge.setChargeNum(chargeNum);
            // 新增
            chargeMapper.insert(charge);
        } else {
            // 更新
            chargeMapper.updateById(charge);
        }
        Long chargeId = charge.getChargeId();
        return chargeMapper.selectById(chargeId);
    }

    @Override
    public void deleteCharge(Long chargeId) {
        LambdaUpdateWrapper<Charge> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(Charge::getChargeId, chargeId);
        wrapper.set(Charge::getDeleteFlag, BaseConstants.Flag.Y);
        chargeMapper.update(null, wrapper);
    }

    @Override
    public List<ChargeDTO> selectYearData(String year, Long userId) {
        LambdaQueryWrapper<Charge> wrapper = new LambdaQueryWrapper<>();
        wrapper.apply("date_format(date, '%Y') = '" + year + "'");
        wrapper.eq(Charge::getDeleteFlag, BaseConstants.Flag.N);
        wrapper.eq(Charge::getCreatedBy, userId);
        List<Charge> chargeList = chargeMapper.selectList(wrapper);
        chargeList.forEach(charge -> {
            String monthDate = DateFormatUtils.format(charge.getDate(), "yyyy-MM");
            charge.setMonthStr(monthDate);
        });
        List<ChargeDTO> chargeDTOList = ChargeDTO.initYearChargeDTO(year);
        Map<String, List<Charge>> chargeMap = chargeList.stream().collect(Collectors.groupingBy(Charge::getMonthStr));
        for (Map.Entry<String, List<Charge>> entry : chargeMap.entrySet()) {
            String month = entry.getKey();
            for (ChargeDTO chargeDTO : chargeDTOList) {
                if (chargeDTO.getMonth().equals(month)) {
                    List<Charge> charges = entry.getValue();
                    // 分组区分 收入 or 支出？
                    if (chargeDTO.getType().equals(BaseConstants.ChargeType.IN)) {
                        // 收入对象
                        BigDecimal inChargeAmount = charges.stream().filter(charge -> BaseConstants.ChargeType.IN.equals(charge.getType()))
                                .map(Charge::getAmount)
                                .reduce(BigDecimal::add)
                                .orElse(BigDecimal.ZERO);
                        chargeDTO.setAmount(inChargeAmount);
                    } else if (chargeDTO.getType().equals(BaseConstants.ChargeType.OUT)) {
                        // 支出对象
                        BigDecimal outChargeAmount = charges.stream().filter(charge -> BaseConstants.ChargeType.OUT.equals(charge.getType()))
                                .map(Charge::getAmount)
                                .reduce(BigDecimal::add)
                                .orElse(BigDecimal.ZERO);
                        chargeDTO.setAmount(outChargeAmount);
                    }
                }
            }
        }
        // 翻译type
        chargeDTOList.forEach(chargeDTO -> chargeDTO.setType(BaseConstants.TYPE_MAP.get(chargeDTO.getType())));
        return chargeDTOList;
    }

    @Override
    public void exportExpense(HttpServletResponse response, String year, Long userId) {
        String fileName = year + "年度分类明细.xlsx";
        Map<String, Map<String, BigDecimal>> dataMap = new HashMap<>();
        LambdaQueryWrapper<Charge> wrapper = new LambdaQueryWrapper<>();
        wrapper.apply("date_format(date, '%Y') = '" + year + "'");
        wrapper.eq(Charge::getDeleteFlag, BaseConstants.Flag.N);
        wrapper.eq(Charge::getCreatedBy, userId);
        List<Charge> chargeList = chargeMapper.selectList(wrapper);
        chargeList.forEach(charge -> {
            String monthDate = DateFormatUtils.format(charge.getDate(), "yyyy-MM");
            charge.setMonthStr(monthDate);
        });
        Map<String, List<Charge>> chargeMap = chargeList.stream().collect(Collectors.groupingBy(Charge::getMonthStr));
        for (int i = 1; i <= 12; i++) {
            Map<String, BigDecimal> monthAmountMap = new HashMap<>(12);
            String currentMonth = year + "-" + (i < 10 ? "0" + i : String.valueOf(i));
            List<Charge> currentChargeList = chargeMap.get(currentMonth);
            if (CollectionUtils.isNotEmpty(currentChargeList)) {
                Map<String, List<Charge>> categoryMap = currentChargeList.stream().collect(Collectors.groupingBy(Charge::getCategory));
                for (Map.Entry<String, List<Charge>> entry : categoryMap.entrySet()) {
                    String key = entry.getKey();
                    List<Charge> value = entry.getValue();
                    if (CollectionUtils.isNotEmpty(value)) {
                        BigDecimal sumAmount = value.stream().map(Charge::getAmount)
                                .reduce(BigDecimal::add)
                                .orElse(null);
                        monthAmountMap.put(key, sumAmount);
                    } else {
                        monthAmountMap.put(key, null);
                    }
                }
            }
            dataMap.put(i + "月", monthAmountMap);
        }
        try {
            List<Category> categoryList = categoryService.list();
            ExcelExportUtil.export(response, dataMap, categoryList, fileName);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public List<CategoryCountDTO> categoryCount(String year, Long userId) {
        LambdaQueryWrapper<Charge> wrapper = new LambdaQueryWrapper<>();
        wrapper.apply("date_format(date, '%Y') = '" + year + "'");
        wrapper.eq(Charge::getDeleteFlag, BaseConstants.Flag.N);
        wrapper.eq(Charge::getCreatedBy, userId);
        List<Charge> chargeList = chargeMapper.selectList(wrapper);
        Map<String, List<Charge>> chargeMap = chargeList.stream().collect(Collectors.groupingBy(Charge::getCategory));
        List<CategoryCountDTO> countDTOList = new ArrayList<>();
        for (Map.Entry<String, List<Charge>> entry : chargeMap.entrySet()) {
            CategoryCountDTO categoryCountDTO = new CategoryCountDTO();
            String type = entry.getKey();
            List<Charge> charges = entry.getValue();
            categoryCountDTO.setCategoryType(type);
            categoryCountDTO.setCount(charges.size());
            BigDecimal amount = charges.stream().map(Charge::getAmount).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            categoryCountDTO.setAmount(amount);
            countDTOList.add(categoryCountDTO);
        }
        // 翻译type
        Map<String, String> categoryMap = categoryMapper.selectList(new QueryWrapper<>())
                .stream().collect(HashMap::new, (m, v) -> m.put(v.getValue(), v.getLabel()), HashMap::putAll);
        countDTOList.forEach(countDTO -> countDTO.setCategoryType(categoryMap.get(countDTO.getCategoryType())));
        return countDTOList;
    }
}
